﻿using System;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.IO.IsolatedStorage;
using System.IO;
using System.Runtime.Serialization;
using System.Collections.Generic;
using System.Xml.Linq;
using System.Xml;

namespace MichMan.Utilities
{
    public class FileManager
    {
        private FileManager()
        {
        }

        public static FileManager Instance
        {
            get
            {
                return Singleton<FileManager>.Create(() => new FileManager());
            }
        }

        public event EventHandler StorageCleared;

        public void ClearData()
        {
            ManagedFiles.Clear();
            IsolatedStorageFile.GetUserStoreForApplication().Remove();

            if (StorageCleared != null)
            {
                StorageCleared(this, EventArgs.Empty);
            }
        }

        /// <summary>
        /// Returns true if the file is managed by the file manager.
        /// Note:  This does not mean that the file exists, just that the FileManager has some metadata about it.
        /// </summary>
        /// <param name="path"></param>
        /// <returns></returns>
        public bool IsManagedFile(string path)
        {
            return this.ManagedFiles.Any(mfi => mfi.Path == path);
        }

        internal ManagedFileInfo FindByUri(string uri)
        {
            if (uri == null)
            {
                return null;
            }
            return ManagedFiles.FirstOrDefault(mfi => mfi.Uri == uri);
        }

        public List<ManagedFileInfo> GetFilesWithMetadata<T>() where T : class
        {
            return GetFilesWithMetadata<T>(typeof(T).FullName);
        }

        public List<ManagedFileInfo> GetFilesWithMetadata<T>(string key) where T : class
        {
            return ManagedFiles.Where(mfi => mfi.Metadata.ContainsKey(key)).ToList();
        }

        internal ManagedFileInfo FindByPath(string path)
        {
            if (path == null)
            {
                return null;
            }
            return ManagedFiles.FirstOrDefault(mfi => mfi.Path == path);
        }

        private ManagedFileInfo FindEntry(string uri, string path)
        {
            return ManagedFiles.FirstOrDefault(mfi => mfi.Uri == uri && mfi.Path == path);
        }

        public ManagedFileInfo TempFileInfo(string url)
        {
            ManagedFileInfo mfi = FindByUri(url);
            if (mfi != null)
            {
                return mfi;
            }

            // Make up a file name.  Old school style!
            string path = System.IO.Path.Combine( "temp", GuidUtility.GuidToStorageName(Guid.NewGuid()) + "~");
            return NewFileInfo(url, path);
        }

        public ManagedFileInfo NewFileInfo(string url, string path)
        {
            if (FindByPath(path) != null || FindByUri(url) != null)
            {
                return null;
            }

            if (path == null)
            {
                // Make up a file name.  Old school style!
                path = GuidUtility.GuidToStorageName(Guid.NewGuid()) + "~";
            }

            ManagedFileInfo mfi = new ManagedFileInfo() { Uri = url, Path = path };
            ManagedFiles.Add(mfi);
            return mfi;
        }

        /// <summary>
        /// Note:  Does not delete the file.  Just removes it from the list of managed files.
        /// </summary>
        /// <param name="mfi"></param>
        /// <returns></returns>
        public bool Remove(ManagedFileInfo mfi)
        {
            return ManagedFiles.Remove(mfi);
        }

        public bool Add(ManagedFileInfo mfi)
        {
            if (FindByPath(mfi.Path) != null || FindByUri(mfi.Uri) != null)
            {
                return false;
            }
            ManagedFiles.Add(mfi);
            return true;
        }

        private FileManagerSettings FileManagerSettings
        {
            get
            {
                return Settings<FileManagerSettings>.Instance;
            }
        }

        private ManagedFiles ManagedFiles { get { return FileManagerSettings.ManagedFiles; } }
    }

    [DataContract]
    public class FileManagerSettings
    {
        public FileManagerSettings()
        {
            this.ManagedFiles = new ManagedFiles();
        }

        [DataMember]
        public ManagedFiles ManagedFiles { get; set; }

        [OnDeserialized()]
        public void OnDeserializedMethod(StreamingContext context)
        {
            if (this.ManagedFiles == null)
            {
                this.ManagedFiles = new ManagedFiles();
            }
        }

    }

    [CollectionDataContract(ItemName="File")]
    public class ManagedFiles : List<ManagedFileInfo>
    {
    }

    [DataContract]
    public class ManagedFileInfo
    {
        public ManagedFileInfo()
        {
            Metadata = new Metadata();
            IsComplete = true;
        }

        [DataMember]
        public string Path { get; set; }
        [DataMember]
        public string Uri { get; set; }
        [DataMember]
        public bool IsComplete { get; set; }

        [DataMember]
        public Metadata Metadata { get; set; }

    }

    [CollectionDataContract(ItemName="Entry")]
    public class Metadata : Dictionary<string, XElement>
    {
        [IgnoreDataMember]
        private Dictionary<string, object> strongTypeCache = new Dictionary<string, object>();

        // Prior to saivng, copy the strongTypeCache object to the base dictionary.
        [OnSerializing()]
        public void OnSerializingMethod(StreamingContext context)
        {
            foreach (string k in strongTypeCache.Keys)
            {
                this[k] = DataContractUtility.DataContractToXElement(strongTypeCache[k]);
            }
        }

        public T Get<T>() where T : class
        {
            return Get<T>(typeof(T).FullName);
        }

        public T Get<T>(string key) where T : class
        {
            T md = null;
            if (!strongTypeCache.ContainsKey(key) && ContainsKey(key))
            {
                md = DataContractUtility.XElementToDataContract<T>(this[key]);
                strongTypeCache.Add(key, md);
            }
            if (strongTypeCache.ContainsKey(key) && ContainsKey(key))
            {
                md = (T)strongTypeCache[key];
            }
            return md;
        }

        public void Put<T>(T obj) where T : class
        {
            Put(typeof(T).FullName, obj);
        }

        public void Put<T>(string key, T obj) where T : class
        {
            this[key] = DataContractUtility.DataContractToXElement(obj);
            strongTypeCache[key] = obj;
        }
    }
}
